//
//  LBTrackManager.m
//  LBTrackManager
//
//  Created by Apple on 2021/9/17.
//

#import "LBTrackManager.h"
#import <Aspects/Aspects.h>
#import "UIScrollView+LBTrackHelper.h"
#import "LBTrackHelper.h"
#import <objc/runtime.h>

///默认有效曝光点停止时间间隔阈值

static NSTimeInterval effectiveTimeThreshhold = 500;
///埋点助手
static __kindof LBTrackHelper *lbhelper = nil;

@implementation LBTrackManager

+ (void)configTrackHelper:(LBTrackHelper *)helper
{
    lbhelper = helper;
    [self track_registerScrollView];
}

+ (void)configEffectiveTimeThreshold:(NSTimeInterval)timeThreshold
{
    effectiveTimeThreshhold = timeThreshold;
}

+ (void)track_registerScrollView
{
    NSError *error = nil;
    [UIScrollView aspect_hookSelector:@selector(didMoveToWindow) withOptions:AspectPositionAfter usingBlock:^(id<AspectInfo> data){
        UIScrollView *scrollView = (UIScrollView *)[data instance];
        if ([scrollView isKindOfClass:[UITableView class]]) {
            UITableView *tableView = (UITableView *)scrollView;
            [self unEffectiveTrackWithTableView:tableView];
        } else if ([scrollView isKindOfClass:[UICollectionView class]]) {
            UICollectionView *collectionView = (UICollectionView *)scrollView;
            [self unEffectionTrackWithCollectionView:collectionView];
        }
    } error:&error];
}


#pragma mark - retister tableview

+ (void)trackRegisterTableView:(UITableView *)tableView tableViewDelegate:(nonnull id<UIScrollViewDelegate>)delegate
{
    [self track_tableViewReload:tableView option:AspectPositionInstead];
    if (![delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewDidEndDecelerating:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewDidEndDecelerating:),tmpImp, [@"v@:@" UTF8String]);
        if (result) {
            [self track_tableViewImpression_scrollViewDidEndDecelerating:AspectPositionBefore presenter:(NSObject *)delegate];
        }
    } else {
        [self track_tableViewImpression_scrollViewDidEndDecelerating:AspectPositionBefore presenter:delegate];
    }
    
    if (![delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewDidEndDragging:willDecelerate:),tmpImp, [@"v@:@@" UTF8String]);
        if (result) {
            [self track_tableViewImpression_scrollViewDidEndDragging:AspectPositionBefore presenter:delegate];
        }
    } else {
        [self track_tableViewImpression_scrollViewDidEndDragging:AspectPositionBefore presenter:delegate];
    }
    
    if (![delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewWillBeginDragging:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewWillBeginDragging:),tmpImp, [@"v@:@" UTF8String]);
        if (result) {
            [self track_tableViewImpression_scrollViewBeginDragging:AspectPositionBefore delegate:delegate];
        }
    } else {
        [self track_tableViewImpression_scrollViewBeginDragging:AspectPositionBefore delegate:delegate];
    }
}

#pragma mark - register collectionView
+ (void)trackRegisterCollection:(UICollectionView *)collectionView collectionViewDelegate:(id<UIScrollViewDelegate>)delegate
{
    [self track_collectionViewReload:collectionView option:AspectPositionInstead];
    if (![delegate respondsToSelector:@selector(scrollViewDidEndDecelerating:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewDidEndDecelerating:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewDidEndDecelerating:),tmpImp, [@"v@:@" UTF8String]);
        if (result) {
            [self track_collectionViewImpression_scrollViewDidEndDecelerating:AspectPositionBefore presenter:(NSObject *)delegate];
        }
    } else {
        [self track_collectionViewImpression_scrollViewDidEndDecelerating:AspectPositionBefore presenter:delegate];
    }
    
    if (![delegate respondsToSelector:@selector(scrollViewDidEndDragging:willDecelerate:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewDidEndDragging:willDecelerate:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewDidEndDragging:willDecelerate:),tmpImp, [@"v@:@@" UTF8String]);
        if (result) {
            [self track_collectionViewImpression_scrollViewDidEndDragging:AspectPositionBefore presenter:delegate];
        }
    } else {
        [self track_collectionViewImpression_scrollViewDidEndDragging:AspectPositionBefore presenter:delegate];
    }
    
    if (![delegate respondsToSelector:@selector(scrollViewWillBeginDragging:)]) {
        IMP tmpImp = [self methodForSelector:@selector(scrollViewWillBeginDragging:)];
        BOOL result = class_addMethod([delegate class], @selector(scrollViewWillBeginDragging:),tmpImp, [@"v@:@" UTF8String]);
        if (result) {
            [self track_collectionViewImpression_scrollViewBeginDragging:AspectPositionBefore delegate:delegate];
        }
    } else {
        [self track_collectionViewImpression_scrollViewBeginDragging:AspectPositionBefore delegate:delegate];
    }
}

#pragma mark - private table related

+ (void)track_tableViewReload:(UITableView *)tableView
                       option:(AspectOptions)options
{
    NSError *error = nil;
    __weak typeof(tableView) weakTableview = tableView;
    [tableView aspect_hookSelector:@selector(reloadData) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> data){
        NSInvocation *invocation = [data originalInvocation];
        [invocation invoke];
        NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
        NSTimeInterval time = [date timeIntervalSince1970]*1000;//
        weakTableview.preScrollEndTime = time;
        NSMutableArray *cellsArray = [self unEffectiveCellIndexPathsArrayWithTableView:weakTableview];
        [lbhelper unEffectiveImpressionWithTableView:weakTableview
                                 cellIndexPathsArray:cellsArray];
    } error:&error];
}

/// tableView滑动停止
/// @param options hook类型
+ (void)track_tableViewImpression_scrollViewDidEndDecelerating:(AspectOptions)options
                                                     presenter:(NSObject *)presenter;
{
    NSError *error = nil;
    [presenter aspect_hookSelector:@selector(scrollViewDidEndDecelerating:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UITableView *tableView = [data arguments].firstObject;
        [self unEffectiveTrackWithTableView:tableView];
    } error:&error];
}

///拖动停止, 这里需要注意，要在不减速的情况下
+ (void)track_tableViewImpression_scrollViewDidEndDragging:(AspectOptions)options
                                                 presenter:(NSObject *)presenter;
{
    NSError *error = nil;
    [(NSObject *)presenter aspect_hookSelector:@selector(scrollViewDidEndDragging:willDecelerate:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UITableView *tableView = [data arguments].firstObject;
        
        BOOL decelerate = [[data arguments].lastObject boolValue];
        if (!decelerate) {
            [self unEffectiveTrackWithTableView:tableView];
        }
    } error:&error];
}

///开始拖动， 进行埋有效点
+ (void)track_tableViewImpression_scrollViewBeginDragging:(AspectOptions)options
                                                 delegate:(NSObject *)delegate
{
    NSError *error = nil;
    [(NSObject *)delegate aspect_hookSelector:@selector(scrollViewWillBeginDragging:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UITableView *tableView = [data arguments].firstObject;
        NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
        NSTimeInterval time = [date timeIntervalSince1970]*1000;//
        if (time - tableView.preScrollEndTime > effectiveTimeThreshhold) {
            ///时间差大于阈值，才进行有效曝光埋点
            [lbhelper effectiveImpressionWithTableView:tableView
                                   cellIndexPathsArray:tableView.preEffectiveCellIndexPathsArray];
        }
    } error:&error];
    
}

+ (void)unEffectiveTrackWithTableView:(UITableView *)tableView
{
    NSMutableArray *cellsArray = [self unEffectiveCellIndexPathsArrayWithTableView:tableView];
    NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
    NSTimeInterval time = [date timeIntervalSince1970]*1000;//
    tableView.preScrollEndTime = time;
    [lbhelper unEffectiveImpressionWithTableView:tableView
                             cellIndexPathsArray:cellsArray];
}
+ (NSMutableArray <NSIndexPath *> *)unEffectiveCellIndexPathsArrayWithTableView:(UITableView *)tableView
{
    NSMutableArray *cellsArray = [self cellArrayWithTableView:tableView];
    NSMutableArray *preEndScrollIndexPathsArray = [NSMutableArray array];
    NSMutableArray *preEffectiveIndexPathsArray = [NSMutableArray array];
    [cellsArray enumerateObjectsUsingBlock:^(UITableViewCell * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        /*数据去重， 这里使用indexpath 对比进行去重，没有直接使用cell， 因为
         cell涉及到重用，直接用cell来比较不准确
        */
        NSIndexPath *cellIndexPath = [tableView indexPathForCell:obj];
        __block BOOL hasBeenInPreIndexPathArray = NO;
        [tableView.preEndScrollCellIndexPathsArray enumerateObjectsUsingBlock:^(NSIndexPath *preIndex, NSUInteger idx, BOOL * _Nonnull stop) {
            if (cellIndexPath.row == preIndex.row &&
                 cellIndexPath.section == preIndex.section) {
                hasBeenInPreIndexPathArray = YES;
            }
        }];
        if (!hasBeenInPreIndexPathArray) {
            [preEffectiveIndexPathsArray addObject:cellIndexPath];
        } 
        [preEndScrollIndexPathsArray addObject:cellIndexPath];
    }];
    tableView.preEffectiveCellIndexPathsArray = preEffectiveIndexPathsArray;
    tableView.preEndScrollCellIndexPathsArray = preEndScrollIndexPathsArray;
    return preEffectiveIndexPathsArray;
}

+ (NSMutableArray <UITableViewCell *> *)cellArrayWithTableView:(UITableView *)tableView
{
    NSMutableArray *cellsArray = [NSMutableArray new];
    
    for (UITableViewCell *cell in tableView.visibleCells) {
        BOOL isRealShow = CGRectContainsRect(tableView.bounds, cell.frame);
        if (!isRealShow) {
            continue;
        }
        [cellsArray addObject:cell];
    }
    return cellsArray;
}

#pragma mark - private collection related

+ (void)track_collectionViewReload:(UICollectionView *)collectionView
                            option:(AspectOptions)options
{
    NSError *error = nil;
    __weak typeof(collectionView) weakCollectionView = collectionView;
    [collectionView aspect_hookSelector:@selector(reloadData) withOptions:AspectPositionInstead usingBlock:^(id<AspectInfo> data){
        NSInvocation *invocation = [data originalInvocation];
        [invocation invoke];
        NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
        NSTimeInterval time = [date timeIntervalSince1970]*1000;//
        weakCollectionView.preScrollEndTime = time;
        NSMutableArray *cellsArray = [self unEffectiveCellIndexPathsArrayWithCollectionView:weakCollectionView];
        [lbhelper unEffectiveImpressionWithCollectionView:weakCollectionView
                                      cellIndexPathsArray:cellsArray];
    } error:&error];
}

/// collectionView滑动停止
/// @param options hook类型
+ (void)track_collectionViewImpression_scrollViewDidEndDecelerating:(AspectOptions)options
                                                     presenter:(NSObject *)presenter;
{
    NSError *error = nil;
    [presenter aspect_hookSelector:@selector(scrollViewDidEndDecelerating:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UICollectionView *collectionView = [data arguments].firstObject;
        [self unEffectionTrackWithCollectionView:collectionView];
    } error:&error];
}

///拖动停止, 这里需要注意，要在不减速的情况下
+ (void)track_collectionViewImpression_scrollViewDidEndDragging:(AspectOptions)options
                                                      presenter:(NSObject *)presenter;
{
    NSError *error = nil;
    [(NSObject *)presenter aspect_hookSelector:@selector(scrollViewDidEndDragging:willDecelerate:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UICollectionView *collectionView = [data arguments].firstObject;
        BOOL decelerate = [[data arguments].lastObject boolValue];
        if (!decelerate) {
            [self unEffectionTrackWithCollectionView:collectionView];
        }
    } error:&error];
}

///开始拖动， 进行埋有效点
+ (void)track_collectionViewImpression_scrollViewBeginDragging:(AspectOptions)options
                                                      delegate:(NSObject *)delegate
{
    NSError *error = nil;
    [(NSObject *)delegate aspect_hookSelector:@selector(scrollViewWillBeginDragging:) withOptions:options usingBlock:^(id<AspectInfo> data){
        UICollectionView *collectionView = [data arguments].firstObject;
        NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
        NSTimeInterval time = [date timeIntervalSince1970]*1000;//
        if (time - collectionView.preScrollEndTime > effectiveTimeThreshhold) {
            ///时间差大于阈值，才进行有效曝光埋点
            [lbhelper effectiveImpressionWithCollectionView:collectionView
                                        cellIndexPathsArray:collectionView.preEffectiveCellIndexPathsArray];
        }
    } error:&error];
    
}

+ (void)unEffectionTrackWithCollectionView:(UICollectionView *)collectionView
{
    NSMutableArray *cellsArray = [self unEffectiveCellIndexPathsArrayWithCollectionView:collectionView];
    NSDate* date = [NSDate dateWithTimeIntervalSinceNow:0];//获取当前时间0秒后的时间
    NSTimeInterval time = [date timeIntervalSince1970]*1000;//
    collectionView.preScrollEndTime = time;
    [lbhelper unEffectiveImpressionWithCollectionView:collectionView
                                  cellIndexPathsArray:cellsArray];
}

+ (NSMutableArray <NSIndexPath *> *)unEffectiveCellIndexPathsArrayWithCollectionView:(UICollectionView *)collectionView
{
    NSMutableArray *cellsArray = [self cellArrayWithCollectionView:collectionView];
    NSMutableArray *preEndScrollIndexPathsArray = [NSMutableArray array];
    NSMutableArray *preEffectiveIndexPathsArray = [NSMutableArray array];
    [cellsArray enumerateObjectsUsingBlock:^(UICollectionViewCell * obj, NSUInteger idx, BOOL * _Nonnull stop) {
        /*数据去重， 这里使用indexpath 对比进行去重，没有直接使用cell， 因为
         cell涉及到重用，直接用cell来比较不准确
        */
        NSIndexPath *cellIndexPath = [collectionView indexPathForCell:obj];
        __block BOOL hasBeenInPreIndexPathArray = NO;
        [collectionView.preEndScrollCellIndexPathsArray enumerateObjectsUsingBlock:^(NSIndexPath *preIndex, NSUInteger idx, BOOL * _Nonnull stop) {
            if (cellIndexPath.item == preIndex.item &&
                 cellIndexPath.section == preIndex.section) {
                hasBeenInPreIndexPathArray = YES;
            }
        }];
        if (!hasBeenInPreIndexPathArray) {
            [preEffectiveIndexPathsArray addObject:cellIndexPath];
        }
        [preEndScrollIndexPathsArray addObject:cellIndexPath];
    }];
    collectionView.preEffectiveCellIndexPathsArray = preEffectiveIndexPathsArray;
    collectionView.preEndScrollCellIndexPathsArray = preEndScrollIndexPathsArray;
    return preEffectiveIndexPathsArray;
}

+ (NSMutableArray <UITableViewCell *> *)cellArrayWithCollectionView:(UICollectionView *)collectionView
{
    NSMutableArray *cellsArray = [NSMutableArray new];
    [collectionView setNeedsLayout];
    [collectionView layoutIfNeeded];
    for (UICollectionViewCell *cell in collectionView.visibleCells) {
        BOOL isRealShow = CGRectContainsRect(collectionView.bounds, cell.frame);
        if (!isRealShow) {
            continue;
        }
        [cellsArray addObject:cell];
    }
    return cellsArray;
}


#pragma mark - hook selector

+ (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    
}

+ (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
    
}

+ (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
    
}

@end
